package ru.ifmo.swing.genetics.config.operator;

import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.EvaluatedCandidate;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;
import org.uncommons.watchmaker.swing.NumericParameterControl;
import org.uncommons.watchmaker.swing.ProbabilityParameterControl;
import ru.ifmo.automaton.Automaton;
import ru.ifmo.common.Source;
import ru.ifmo.genetics.adaptation.AdaptableParameter;
import ru.ifmo.genetics.mutation.string.DistributionBasedComplexStringMutation;
import ru.ifmo.swing.EvolutionControlWithConfigProvider;
import ru.ifmo.swing.Panel;
import ru.ifmo.swing.ProgressStartListener;

import javax.swing.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author avhaliullin
 */
public class AutomatonBasedStringMutationControl implements
        EvolutionaryOperator<String>,
        EvolutionControlWithConfigProvider,
        ProgressStartListener,
        AdaptableParameter<String> {
    private DistributionBasedComplexStringMutation mutation;
    private final CorrectingProbabilityControl probabilityControl;
    private final ProbabilityParameterControl localProbabilityControl;
    private final Source<List<Character>> alphabetProvider;
    private final NumberGenerator<Integer> maxLengthProvider;
    private final Panel control;
    private final NumberGenerator<Integer> symbolGenerator;

    public AutomatonBasedStringMutationControl(double defaultMutationProbability,
                                               Source<Automaton<Character, String>> automatonSource,
                                               Source<List<Character>> alphabetProvider, NumberGenerator<Integer> maxLengthProvider,
                                               NumberGenerator<Integer> symbolGenerator) {
        this.alphabetProvider = alphabetProvider;
        this.maxLengthProvider = maxLengthProvider;
        this.symbolGenerator = symbolGenerator;

        localProbabilityControl = new ProbabilityParameterControl(new Probability(defaultMutationProbability));

        probabilityControl = new CorrectingProbabilityControl(automatonSource, defaultMutationProbability, 0.001);

        control = new Panel();
        control.setHorizontal(false);

        probabilityControl.setDescription("Probability");
        control.add(probabilityControl.getControl());

        Panel localProbability = new Panel();
        localProbability.setHorizontal(true);
        localProbability.add(new JLabel("Local probability: "));
        localProbability.add(localProbabilityControl.getControl());
        control.add(localProbability);

    }

    public JComponent getControl() {
        return control;
    }

    public void reset() {
        probabilityControl.reset();
    }

    public void setDescription(String description) {
        control.setTitle(description);
    }

    public List<String> apply(List<String> selectedCandidates, Random rng) {
        return mutation.apply(selectedCandidates, rng);
    }

    public void progressStarted() {
        mutation = new DistributionBasedComplexStringMutation(probabilityControl,
                alphabetProvider.create(),
                symbolGenerator,
                maxLengthProvider,
                localProbabilityControl.getNumberGenerator());
    }

    public void adapt(List<List<EvaluatedCandidate<String>>> lastGenerations) {
        probabilityControl.adapt(lastGenerations);
    }

    public Map<String, Object> getParamValues() {
        Map<String, Object> res = new HashMap<String, Object>();
        res.put("Probability", probabilityControl);
        res.put("Local probability", localProbabilityControl.getNumberGenerator().nextValue());
        return res;
    }
}
